import java.awt.Dialog.ModalExclusionType;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;

import javax.swing.*;


public class XmlEditFrame extends JFrame implements WindowFocusListener, WindowListener, 
	ActionListener, FocusListener{
	static final JFileChooser FileChooser = new JFileChooser();

	final PNSimFrame ParentFrame;
	final PNTransition Trans;
	
	final KeyValueList InitialState;
	
	GridBagLayout mainLayout = new GridBagLayout();
	GridLayout msgIdLayout = new GridLayout(1, 2);
	GridBagLayout kvLayout    = new GridBagLayout();
	GridBagConstraints keySetup = new GridBagConstraints();
	GridBagConstraints valSetup = new GridBagConstraints();
	GridLayout ctrlLayout  = new GridLayout(2,  2);

	JPanel mainPanel = new JPanel(mainLayout);
	JPanel infoPanel = new JPanel();
	JPanel msgIdPanel = new JPanel(msgIdLayout);
	JPanel kvPanel = new JPanel(kvLayout);
	JScrollPane kvScroll = new JScrollPane(kvPanel);
	JPanel ctrlPanel = new JPanel(ctrlLayout);
	
	JLabel infoLabel = new JLabel();
	JLabel msgIdLabel = new JLabel("MsgID: ");
	JTextField msgIdTxt = new JTextField();
	ArrayList<KVKeyField> keys = new ArrayList<KVKeyField>();
	//ArrayList<JTextField> vals = new ArrayList<JTextField>();
	JButton addBtn = new JButton("Add Key/Value");
	JButton loadBtn = new JButton("Load Xml Msg...");
	JButton clearBtn = new JButton("Clear Msg");

	private boolean noClose = false;
	private boolean needsSave = false;
	
	public XmlEditFrame(PNSimFrame simFrame, PNTransition targetTrans){
		ParentFrame = simFrame;
		Trans = targetTrans;
		if(Trans.OnFire == null)
			InitialState = targetTrans.OnFire;
		else {
			InitialState = new KeyValueList();
			for(int i=0; i<Trans.OnFire.size(); i++)
				InitialState.addPair(Trans.OnFire.keyAt(i), Trans.OnFire.valueAt(i));
		}
		this.setTitle("Xml Msg Edit - (" + Trans.Id + ")");
		this.add(mainPanel);
		GridBagConstraints mainSetup = new GridBagConstraints();
		mainSetup.weightx = 1;
		mainSetup.gridwidth = 1;
		mainSetup.gridheight = 1;
		mainSetup.gridx = 0;
		mainSetup.ipady = 5;

		mainSetup.anchor = GridBagConstraints.NORTH;
		mainSetup.fill = GridBagConstraints.HORIZONTAL;
		mainSetup.gridy = 0;
		mainSetup.weighty = 0;
		mainPanel.add(infoPanel, mainSetup);
		mainSetup.anchor = GridBagConstraints.NORTH;
		mainSetup.fill = GridBagConstraints.HORIZONTAL;
		mainSetup.gridy = 1;
		mainSetup.weighty = 0;
		mainPanel.add(msgIdPanel, mainSetup);
		mainSetup.anchor = GridBagConstraints.CENTER;
		mainSetup.fill = GridBagConstraints.BOTH;
		mainSetup.gridy = 2;
		mainSetup.weighty = 1;
		mainPanel.add(kvScroll, mainSetup);
		mainSetup.anchor = GridBagConstraints.SOUTH;
		mainSetup.fill = GridBagConstraints.HORIZONTAL;
		mainSetup.gridy = 3;
		mainSetup.weighty = 0;
		mainPanel.add(ctrlPanel, mainSetup);

		infoPanel.add(infoLabel);
		infoLabel.setText("Msg sent by Transition " + Trans.Id + ": ");
		if(Trans.OnFire == null)infoLabel.setText(infoLabel.getText() + "none set");
		msgIdPanel.add(msgIdLabel);
		msgIdPanel.add(msgIdTxt);
		kvScroll.setPreferredSize(new Dimension(300, 150));
		keySetup.fill = GridBagConstraints.HORIZONTAL;
		keySetup.weightx = 0.5;
		keySetup.gridx = 0;
		valSetup.fill = GridBagConstraints.HORIZONTAL;
		valSetup.weightx = 1;
		valSetup.gridx = 1;
		addBtn.setActionCommand("add");
		loadBtn.setActionCommand("load");
		clearBtn.setActionCommand("clear");
		addBtn.addActionListener(this);
		loadBtn.addActionListener(this);
		clearBtn.addActionListener(this);
		ctrlPanel.add(addBtn);
		ctrlPanel.add(loadBtn);
		ctrlPanel.add(clearBtn);
		
		
		if(Trans.OnFire != null)
			initWithData(Trans.OnFire);
		else
			initWithoutData();

		this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		msgIdTxt.addFocusListener(this);
		this.addWindowFocusListener(this);
		this.addWindowListener(this);
		this.setLocation(ParentFrame.getX()+ParentFrame.getWidth(), ParentFrame.getY());
		pack();
		setVisible(true);
	}
	
	void initWithData(KeyValueList kv) {
		
		for(int i=0; i<kv.size(); i++) {
			String k = kv.keyAt(i);
			if(("MsgID").equals(k))
				msgIdTxt.setText(kv.getValue(k));
			else {
				keys.add(new KVKeyField(kv, k, new JTextField(kv.getValue(k))));
				kvPanel.add(keys.get(keys.size()-1), keySetup);
				kvPanel.add(keys.get(keys.size()-1).val, valSetup);
				keys.get(keys.size()-1).addFocusListener(this);
				keys.get(keys.size()-1).val.addFocusListener(this);
			}
		}
	}
	
	void initWithoutData() {
		msgIdTxt.setEnabled(false);
		clearBtn.setEnabled(false);
		//delBtn.setEnabled(false);
	}

	void promptForMsgId() {
		try {
			noClose = true;
			int id = Integer.parseInt(JOptionPane.showInputDialog("Enter the MsgID for the Xml Msg:"));
			if(Trans.OnFire == null) {
				Trans.OnFire = new KeyValueList();
				Trans.OnFire.addPair("MsgID", "");
			}			
			Trans.OnFire.setValue(Trans.OnFire.lookupKey("MsgID"), id+"");
			msgIdTxt.setEnabled(true);
			msgIdTxt.setText(id+"");
			//msgIdTxt.addFocusListener(this);
			infoLabel.setText(infoLabel.getText().replaceAll("none set", ""));
			clearBtn.setEnabled(true);
		} catch (NumberFormatException ex) {
		}
		noClose = false;
	}
	
	void removeKVPair(KVKeyField kf) {
		Trans.OnFire.removePair(kf.key);
		needsSave = true;
		keys.remove(kf);
	}
	
	void doSave() {
		//if(InitialState)
	}
	
	@Override
	public void windowGainedFocus(WindowEvent arg0) {}

	@Override
	public void windowLostFocus(WindowEvent arg0) {
		//TODO: handle mid-edit save cases. ugh.
		if(noClose) return;
		//setVisible(false);
		this.requestFocus();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		//button actions!
		if(("add").equals(e.getActionCommand())){
			if(Trans.OnFire == null) {
				this.promptForMsgId();
				if(Trans.OnFire == null)
					return;
			}
			String dummyKey = "key";
			int i = 0;
			while(Trans.OnFire.lookupKey(dummyKey + i) >= 0) i++;
			Trans.OnFire.addPair(dummyKey+i, "");
			keys.add(new KVKeyField(Trans.OnFire, dummyKey+i, new JTextField()));
			kvPanel.add(keys.get(keys.size()-1), keySetup);
			kvPanel.add(keys.get(keys.size()-1).val, valSetup);
			keys.get(keys.size()-1).addFocusListener(this);
			keys.get(keys.size()-1).val.addFocusListener(this);
		}
		if(("load").equals(e.getActionCommand())){
			int returnVal = FileChooser.showOpenDialog(this);
			if(returnVal == JFileChooser.APPROVE_OPTION)
			{
				try{
					File f = FileChooser.getSelectedFile();
					BufferedReader rdr = new BufferedReader(new FileReader(f));
					StringBuilder xmlMsg = new StringBuilder();
					String line;
					while((line = rdr.readLine()) != null) xmlMsg.append(line);
					KeyValueList newKvl = PNTransition.parseXmlMsg(xmlMsg.toString());
					Trans.OnFire = newKvl;
					needsSave = true;
					for(KVKeyField tf : keys){ 
						kvPanel.remove(tf);
						kvPanel.remove(tf.val);
					}
					keys.clear();
					initWithData(Trans.OnFire);
				}catch(Throwable t) {
					JOptionPane.showMessageDialog(ParentFrame, "An error occurred: " + t);
				}
			}
		}
		if(("clear").equals(e.getActionCommand())){
			noClose = true;
			int res = JOptionPane.showConfirmDialog(this, "You are about to delete a Msg action from Transition " + Trans.Id 
					+ ".  This cannot be undone, are you sure?");
			if(res == JOptionPane.YES_OPTION) {
				Trans.OnFire = null;
				needsSave = true;
				for(KVKeyField tf : keys){ 
					kvPanel.remove(tf);
					kvPanel.remove(tf.val);
				}
				keys.clear();
				msgIdTxt.setText("");
				msgIdTxt.setEnabled(false);
			}
			noClose = false;
		}
		pack();
		repaint();		
	}
	
	
	@Override
	public void focusGained(FocusEvent arg0) {	}

	@Override
	public void focusLost(FocusEvent arg0) {
		if(arg0.getComponent().equals(msgIdTxt)) {
			try{
				int newMsgId = Integer.parseInt(msgIdTxt.getText());
				if(newMsgId != Integer.parseInt(Trans.OnFire.getValue("MsgID"))) {
					Trans.OnFire.setValue(Trans.OnFire.lookupKey("MsgID"), newMsgId+"");
					this.needsSave = true;
				}
			}catch(Exception e) { msgIdTxt.setText(Trans.OnFire.getValue("MsgID")); }
		}else
			for(int i=0; i<keys.size(); i++) {
				KVKeyField kvf = keys.get(i);
				if(arg0.getComponent().equals(kvf.val)) { //event comp is Value field
					String newVal = kvf.val.getText();
					if(!newVal.equals(Trans.OnFire.getValue(kvf.getText()))) {
						Trans.OnFire.setValue(Trans.OnFire.lookupKey(kvf.getText()), newVal);
						needsSave = true;
					}
				} else if (arg0.getComponent().equals(kvf)) { //event comp is Key field
					String key = kvf.getText().trim();
					if(key.equals(kvf.key)) //no change in key string
						return;
					else if (Trans.OnFire.lookupKey(key) >= 0) { //changed to an existing key
						JOptionPane.showMessageDialog(this, "This is a duplicate Key");
						kvf.setText(kvf.key);
						return;
					}
					this.removeKVPair(kvf); //sets needsSave=true
					if(key.length() < 1) { 
						kvPanel.remove(kvf.val);
						kvPanel.remove(kvf);
						repaint();
						return;
					}
					Trans.OnFire.addPair(key, kvf.val.getText());
					kvf.setText(kvf.key = key);
					keys.add(kvf);
				}
			}
		
	}

	class KVKeyField extends JTextField {
		KeyValueList kvl;
		String key;
		JTextField val;
		public KVKeyField(KeyValueList kvl, String key, JTextField valField) {
			this.kvl = kvl;
			this.key = key;
			this.val = valField;
			this.setText(key);
			val.setText(kvl.getValue(key));
		}
	}

	public void windowActivated(WindowEvent arg0) {}
	public void windowClosed(WindowEvent arg0) {}

	@Override
	public void windowClosing(WindowEvent arg0) {
		if(needsSave) {
			this.setVisible(true);
			int resp = JOptionPane.showConfirmDialog(this, "Do you wish to save changes?");
			if(resp == JOptionPane.CANCEL_OPTION) 
				return;
			else if(resp == JOptionPane.YES_OPTION) 
				Trans.updateOnFire(this.ParentFrame.PNData);
			else if(resp == JOptionPane.NO_OPTION)
				Trans.OnFire = InitialState;
		}		
		this.setVisible(false);
		this.dispose();
	}

	public void windowDeactivated(WindowEvent arg0) { }
	public void windowDeiconified(WindowEvent arg0) { }
	public void windowIconified(WindowEvent arg0) { }
	public void windowOpened(WindowEvent arg0) { }
}
